const { BusiError, SysError } = require('../exception/exception');
const rsa = require('../encode/Rsa');
const md5 = require('md5');
const config = require('../config/config');
const axi = require('axios');
const iconv = require('iconv-lite');

const uuidUtil = require('../util/uuidUtil');
const xmlUtil = require('../util/xmlUtil');
const dateUtil = require('../util/dateUtil');
const stringUtil = require('../util/stringUtil');
const idUtil = require('../util/idUtil');
const md5Util = require('../util/md5Util');

const payOrderDao = require('../dao/payOrderDao');
const userDao = require('../dao/userDao');
const mysqlPool = require('../dao/MySQLPool');

const payOrderCategoryService = require('./PayOrderCategoryService');

const busiConst = require('../const/busiConst');
const payConst = require('../const/payConst');

const axiosConfig = {
    timeout: 15000,
    baseURL: config.conf["pay.nn.baseUrl"],
};

const axios = axi.create(axiosConfig);
axios.interceptors.response.use(function (response) {
    let ctype = response.headers["content-type"];
    response.data = (ctype.includes("GB2312") || ctype.includes("gb2312")) ? iconv.decode(response.data, 'gb2312') : iconv.decode(response.data, 'utf-8');
    console.logw(response.data);
    return response;
});

class MergeUnionPayService {
    constructor() {
        this.payChannel = "J006";
        this.notifyUrl = config.conf["pay.nn.notifyUrl"] + "/union";
        this.mchId = config.conf["pay.nn.mchId"];
        this.key = config.conf['pay.nn.key'];
        this.service = "fk_union_pay_service";
    }

    /**
     *
     * @param {String} uid
     * @param {int} amount
     * @returns {Promise<void>}
     */
    async pay(uid, amount, category) {
        let payEntity = {};
        payEntity.spid = this.mchId;
        payEntity.notify_url = this.notifyUrl;

        let outTradeNo = busiConst.TYPE_RECHARGE_OUT_TRADE_NO + config.machineNo + dateUtil.dateFormat("yyyyMMddhhmmss", new Date())
            + stringUtil.padZeroSix(idUtil.nextSix());
        payEntity.sp_billno = outTradeNo;
        payEntity.spbill_create_ip = "127.0.0.1";
        payEntity.out_channel = "ylpay";
        payEntity.pay_type = "800201";
        let timeStr = dateUtil.dateFormat("yyyyMMddhhmmss", new Date());
        payEntity.tran_time = timeStr;
        payEntity.tran_amt = amount;
        payEntity.cur_type = "CNY";
        payEntity.item_name = "FK_UNION";

        let expireTime = new Date();//你已知的时间
        let timestamp = expireTime.getTime();//转化为时间戳毫秒数
        expireTime.setTime(timestamp + 1000 * 60 * 60);//设置新时间比旧时间多一小时
        let order = {
            uid: uid,
            payChannel: this.payChannel,
            amount: amount,
            outTradeNo: outTradeNo,
            payOrderType: payConst.ORDER_TYPE_RECHARGE,
            tradeType: this.service,
            expireTime: expireTime,
            category: category
        };

        let saveOrderId = await payOrderDao.save(order);
        if (saveOrderId <= 0) {
            throw new BusiError(500, "创建订单失败");
        }

        order.id = saveOrderId;

        let signStr = stringUtil.getSignStr(payEntity);
        payEntity.sign = md5(signStr + "&key=" + this.key);

        let jsonXml = xmlUtil.entity2xml(payEntity);
        console.logw("创建订单请求报文:", jsonXml);

        let queryStr = signStr + "&sign=" + payEntity.sign.toUpperCase();

        let json2 = await xmlUtil.xml2json(jsonXml);
        let resp = await axios.get("/cgi-bin/v2.0/unite_pay_apply.cgi?" + queryStr, { responseType: 'arraybuffer' });
        let json = await xmlUtil.xml2json(resp.data);
        console.logw("创建订单返回报文:", JSON.stringify(json));

        if (json.root.retcode !== '00') {
            order.errCode = json.root.retcode;
            order.errMsg = json.root.retmsg;
            order.status = payConst.payOrderStatus.orderClose;
            let updateRes = await payOrderDao.updateByOutTradeNo(order);
            if (updateRes <= 0) {
                console.log("更新订单异常信息出错:", order);
                throw new BusiError(500, "更新订单异常信息出错");
            }
            throw new BusiError(500, "交易通信异常");
        }

        //
        let resSignStr = stringUtil.getMD5SignStr(json.root);
        resSignStr += "&key=" + this.key;
        let isValid = await md5Util.verifySign(resSignStr, json.root.sign);
        if (!isValid) {
            console.logw("订单返回报文验签失败:", JSON.stringify(json));
            order.remark = "订单返回报文验签失败";
            let updateRes = await payOrderDao.updateByOutTradeNo(order);
            if (updateRes <= 0) {
                console.log("更新订单异常信息出错:", order);
                throw new BusiError(500, "更新订单异常信息出错");
            }
            throw new BusiError(500, "验签失败");
        }

        order.tradeNo = json.root.listid;
        let updateRes = await payOrderDao.updateByOutTradeNo(order);
        if (updateRes <= 0) {
            console.log("更新订单异常信息出错:", order);
        }

        return {
            payInfo: json.root.payinfo,
            qrcode: json.root.qrcode
        };
    }

    async payNotify(notify) {
       console.log("no impl");
    }

    async payQuery(outTradeNo) {
        let order = await payOrderDao.getByOutTradeNo(outTradeNo);
        if (!order) {
            console.log("receive pay notify err[3], 查无此订单:", outTradeNo);
            throw new BusiError(500, "查询失败");
        }
        order.outTradeNo = outTradeNo;

        if (order.status === payConst.payOrderStatus.orderSuccess) {
            console.log("此订单已经交易成功:", order);
            throw new BusiError(500, "此订单已经交易成功");
        }

        let queryEntity = {};
        queryEntity.spid = this.mchId;
        queryEntity.sp_billno = outTradeNo;

        let signStr = stringUtil.getSignStr(queryEntity);
        let sign = await md5(signStr + "&key=" + this.key);
        // queryEntity.sign = sign;
        // let jsonXml = xmlUtil.entity2xml(queryEntity);
        let queryStr = signStr + "&input_charset=UTF-8" + "&sign=" + sign;
        console.logw("查询订单请求报文:", queryStr);

        // let json2 = await xmlUtil.xml2json(jsonXml);
        let resp = await axios.get("/cgi-bin/v2.0/unite_pay_single_qry.cgi?" + queryStr, { responseType: 'arraybuffer' });
        let json = await xmlUtil.xml2json(resp.data);
        console.logw("查询订单返回报文:", JSON.stringify(json));

        if (json.root.retcode !== '00') {
            order.errMsg = json.root.retmsg;
            let updateRes = await payOrderDao.updateByOutTradeNo(order);
            if (updateRes <= 0) {
                console.log("更新订单异常信息出错:", order);
                throw new BusiError(500, "更新订单异常信息出错:" + json.root.retmsg);
            }
            throw new BusiError(500, "交易通信异常:" + json.root.retmsg);
        }

        let msg = "";
        let data = json.root.data;
        if (!data) {
            throw new BusiError(500, "查询失败");
        }

        if (data.record.state === '4' || data.record.state ==='5') {
            order.errCode = json.root.state + "";
            order.status = payConst.payOrderStatus.orderClose;
            let updateRes = await payOrderDao.updateByOutTradeNo(order);
            if (updateRes <= 0) {
                console.log("更新订单异常信息出错:", order);
                throw new BusiError(500, "更新订单异常信息出错:" + json.root.state);
            }
            throw new BusiError(500, "交易失败");
        }

        if (data.record.state === '3') {
            order.status = payConst.payOrderStatus.orderSuccess;
            if (!data.record.pay_time) {
                order.finishTime = new Date();
            } else {
                order.finishTime = dateUtil.yyyyMMddHHmmssToDate(data.record.pay_time);
            }

            await payOrderCategoryService.orderProcess(order.category, order);
        }

        if (data.record.state !== '3') {
            console.logw("查询订单失败:", JSON.stringify(json));
            // 对超出截止日期5分钟的订单做处理
            let expireTime = new Date(order.expire_time);
            if (expireTime.getTime() + 1000 * 60 * 5 < new Date().getTime()) {
                order.status = payConst.payOrderStatus.orderClose;
                order.remark = "订单超时关闭";
            }

            order.errCode = json.root.state;
            msg = json.root.state + ":" + json.root.retmsg;
            let updateRes = await payOrderDao.updateByOutTradeNo(order);
            if (updateRes <= 0) {
                console.log("更新订单异常信息出错:", order);
                throw new BusiError(500, "更新订单异常信息出错:" + json.root.retmsg);
            }
            throw new BusiError(500, "交易失败:" +  json.root.retmsg);
        }

        // 不用验签
        return msg;
    }

    async refund(outTradeNo, refundAmt) {
        let refundEntity = {};
        refundEntity.service = "unified.trade.refund";
        // refundEntity.version = "1.0";
        // refundEntity.charset = "UTF-8";
        // refundEntity.sign_type = "MD5";
        refundEntity.mch_id = this.mchId;
        refundEntity.out_trade_no = outTradeNo;

        // todo: create refund no
        let outRefundNo = busiConst.TYPE_RECHARGE_OUT_REFUND_NO + config.machineNo + dateUtil.dateFormat("yyyyMMddhhmmss")
            + stringUtil.padZeroSix(idUtil.nextSix());
        refundEntity.out_refund_no = outRefundNo;

        // query db
        let res = {};
        refundEntity.total_fee = res.amount;
        refundEntity.refund_fee = refundAmt;
        refundEntity.op_user_id = this.mchId;
        refundEntity.refund_channel = "ORIGINAL";
        refundEntity.nonce_str = uuidUtil.uuid();


        let signStr = stringUtil.getSignStr(refundEntity);
        let sign = await rsa.sign256(signStr);
        refundEntity.sign = sign;

        let jsonXml = xmlUtil.entity2xml(refundEntity);
        console.log("final str:", jsonXml);

        let resp = await axios.post("", jsonXml);
        console.log("data:", resp.data);

        let json = await xmlUtil.xml2json(resp.data);
        console.log(JSON.stringify(json));
    }

    async refundQuery(outRefundNo) {
        let refundQueryEntity = {};
        refundQueryEntity.service = "unified.trade.refundquery";
        // refundQueryEntity.version = "1.0";
        // refundQueryEntity.charset = "UTF-8";
        // refundQueryEntity.sign_type = "MD5";
        refundQueryEntity.mch_id = this.mchId;
        refundQueryEntity.out_refund_no = outRefundNo;
        refundQueryEntity.nonce_str = uuidUtil.uuid();

        let signStr = stringUtil.getSignStr(refundQueryEntity);
        let sign = await rsa.sign256(signStr);
        refundQueryEntity.sign = sign;

        let jsonXml = xmlUtil.entity2xml(refundQueryEntity);
        console.log("final str:", jsonXml);

        let resp = await axios.post("", jsonXml);
        console.log("data:", resp.data);


        let json = await xmlUtil.xml2json(resp.data);
        console.log(JSON.stringify(json));
    }
}

let mergeUnionPayService = new MergeUnionPayService();
module.exports = mergeUnionPayService;